// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2003 Bernardo Innocenti <bernie@develer.com>
 *
 * Based on former do_div() implementation from asm-parisc/div64.h:
 *	Copyright (C) 1999 Hewlett-Packard Co
 *	Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com>
 *
 *
 * Generic C version of 64bit/32bit division and modulo, with
 * 64bit result and 32bit remainder.
 *
 * The fast case for (n>>32 == 0) is handled inline by do_div().
 *
 * Code generated for this function might be very inefficient
 * for some CPUs. __div64_32() can be overridden by linking arch-specific
 * assembly versions such as arch/ppc/lib/div64.S and arch/sh/lib/div64.S
 * or by defining a preprocessor macro in arch/include/asm/div64.h.
 */

#include <seminix/kernel.h>
#include <seminix/math64.h>

/* Not needed on 64bit architectures */
#if BITS_PER_LONG == 32

#ifndef __div64_32
u32 __attribute__((weak)) __div64_32(u64 *n, u32 base)
{
    u64 rem = *n;
    u64 b = base;
    u64 res, d = 1;
    u32 high = rem >> 32;

    /* Reduce the thing a bit first */
    res = 0;
    if (high >= base) {
        high /= base;
        res = (u64) high << 32;
        rem -= (u64) (high*base) << 32;
    }

    while ((i64)b > 0 && b < rem) {
        b = b+b;
        d = d+d;
    }

    do {
        if (rem >= b) {
            rem -= b;
            res += d;
        }
        b >>= 1;
        d >>= 1;
    } while (d);

    *n = res;
    return rem;
}
#endif

/**
 * div_s64_rem - signed 64bit divide with 64bit divisor and remainder
 * @dividend:	64bit dividend
 * @divisor:	64bit divisor
 * @remainder:  64bit remainder
 */
#ifndef div_s64_rem
i64 div_s64_rem(i64 dividend, s32 divisor, s32 *remainder)
{
    u64 quotient;

    if (dividend < 0) {
        quotient = div_u64_rem(-dividend, abs(divisor), (u32 *)remainder);
        *remainder = -*remainder;
        if (divisor > 0)
            quotient = -quotient;
    } else {
        quotient = div_u64_rem(dividend, abs(divisor), (u32 *)remainder);
        if (divisor < 0)
            quotient = -quotient;
    }
    return quotient;
}
#endif

/**
 * div64_u64_rem - unsigned 64bit divide with 64bit divisor and remainder
 * @dividend:	64bit dividend
 * @divisor:	64bit divisor
 * @remainder:  64bit remainder
 *
 * This implementation is a comparable to algorithm used by div64_u64.
 * But this operation, which includes math for calculating the remainder,
 * is kept distinct to avoid slowing down the div64_u64 operation on 32bit
 * systems.
 */
#ifndef div64_u64_rem
u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder)
{
    u32 high = divisor >> 32;
    u64 quot;

    if (high == 0) {
        u32 rem32;
        quot = div_u64_rem(dividend, divisor, &rem32);
        *remainder = rem32;
    } else {
        int n = 1 + fls(high);
        quot = div_u64(dividend >> n, divisor >> n);

        if (quot != 0)
            quot--;

        *remainder = dividend - quot * divisor;
        if (*remainder >= divisor) {
            quot++;
            *remainder -= divisor;
        }
    }

    return quot;
}
#endif

/**
 * div64_u64 - unsigned 64bit divide with 64bit divisor
 * @dividend:	64bit dividend
 * @divisor:	64bit divisor
 *
 * This implementation is a modified version of the algorithm proposed
 * by the book 'Hacker's Delight'.  The original source and full proof
 * can be found here and is available for use without restriction.
 *
 * 'http://www.hackersdelight.org/hdcodetxt/divDouble.c.txt'
 */
#ifndef div64_u64
u64 div64_u64(u64 dividend, u64 divisor)
{
    u32 high = divisor >> 32;
    u64 quot;

    if (high == 0) {
        quot = div_u64(dividend, divisor);
    } else {
        int n = 1 + fls(high);
        quot = div_u64(dividend >> n, divisor >> n);

        if (quot != 0)
            quot--;
        if ((dividend - quot * divisor) >= divisor)
            quot++;
    }

    return quot;
}
#endif

/**
 * div64_s64 - signed 64bit divide with 64bit divisor
 * @dividend:	64bit dividend
 * @divisor:	64bit divisor
 */
#ifndef div64_s64
i64 div64_s64(i64 dividend, i64 divisor)
{
    i64 quot, t;

    quot = div64_u64(abs(dividend), abs(divisor));
    t = (dividend ^ divisor) >> 63;

    return (quot ^ t) - t;
}
#endif

#endif /* BITS_PER_LONG == 32 */

/*
 * Iterative div/mod for use when dividend is not expected to be much
 * bigger than divisor.
 */
u32 iter_div_u64_rem(u64 dividend, u32 divisor, u64 *remainder)
{
    return __iter_div_u64_rem(dividend, divisor, remainder);
}
